# num = 1
# while num <= 3:
#     print(f'外部', num)
#     num1 = 1
#     while num1 <= 3:
#         print(f'内部', num1)
#         num1 += 1
#     num += 1
# import copy
# import math
# import random
# import string
# import traceback
# from linecache import clearcache
# from logging import exception, raiseExceptions
# from nt import lstat
# from operator import ifloordiv
# import os.path
# import string
# from _ast import pattern
# from operator import truediv

# from encodings.punycode import selective_find

# num = 1
# while num <= 3:
#     num1 = 1
#     while num1 <= 3:
#         num1 += 1
#         print(f'内部', num1)
#     num += 1
# print(f'外部', num)


# num = int(input("请输入年龄: "))
# if 1 <= num <= 18 :
#     print('未成年')
# elif 18< num  <= 65:
#     print('已成年')
# elif 65 < num <= 100:
#     print('老年')
# else:
#     print('输入错误')
#
# for s in '123456':
#     print(s)

# for i in range(1,11):
#     if i==7:
#         continue
#     print(i)

# print('春眠不觉晓，\n处处闻啼鸟。')
#
# print('班级\t年龄\t姓名')
# print('二班\t19\t王')水平制表符

# print(r'D:\pych\.venv\1.py')原生字符，消去\的特殊含义，仅代表路径
#
# s = ' hello '
# print(s.strip())去除首尾空格
# print(s.lstrip())去除左边空格
# print(s.rstrip())右边
#
# s = '1 2 3 4 5 6 7 8 9'
# print(s.split())



# s = 'hello world'替换符前为旧的，后为新字符
# print(s.replace('world','python'))

# lst = ['hello','world']
# s = ' '.join(lst)链接符，使单个元素，组合成字符
# print(s)

# s = 'hello world'
# print(s.find('d'))#查找元素所在位置从0开始，如果没有就是-1

#index和find函数一样
#
# s = 'HELLO WORLD'
# print(s.lower())将大写字母转换为小写字母
#将小写字母转换为大写用.upper()
# 首字母转换成大写：title

# 字符串判断
# 返回值为玻尔值；true or false
# 判断是否全是小写字母；islower
#             大写字母；isupper
# 判断每个字符前第一个字母是否为大写 istitle

# 判断字符串是否为指定字符开头；startswitch
#                      结尾；endswitch
# 是否为空白字符isspace
# 是否有空白字符isalpha

# 计算列表中元素个数；len()

# 列表是一种灵活的数据类型，可以存储一系列数据
# lst = [1,2,3,1,5,6,1,8,'hello']
# # print(lst,type(lst))
# # 布尔值为False，为零为空
# # 为零；整数零，浮点数零，复数零
# # 为空；字符串，空列表，空元组
# #
# # 计算列表中元素出现次数；列表.count
# print(lst.count(1))

# in 判断元素是否在列表中 not in 判断是否不在列表中
# print(1 in  lst)
#
# 列表元素修改
# 列表[索引值] = 新数据
#
# 列表元素增加
# lst = []
# # 通过索引值，插入元素 列表.insert
# # lst.insert(0,'python' )
# # print(lst)
# 尾部追加用append
#
# 添加多个元素， 列表.extend
# 字符串是分开一个一个加的
#
# 用函数查找所在元素的索引值  列表.index
# 找不到会报错


# 通过索引值删除元素；del.列表[索引值]
# del 变量名    可以删除变量
# pop只能删除列表中的元素
# remove可以删除指定元素，存在多个满足条件的元素。只删除第一个
# # 可以利用循环全部删除
# clear可以删除列表里所有元素，但不删除列表。

# 列表的排序（原地排序）
# 默认对列表进行从小到大的排序  列表.sort()
# 从大到小是 列表.sort(reverse=True)
# 根据字符串长度排序  列表.sort(key=len)
# 数字的排序 sorted()默认从小到大
#
# 不改变原列表顺序进行排序
# sorted(想要排序的可迭代对象，key=,reverse = Ture)
# 要赋值新变量才能显示排序后的操作w
# key = lamda 形参：["返回值"](形参；没有实际赋值  实参：赋值的具体数据)

# 列表推导式
# [表达式 for 临时变量 in 可迭代对象 if 判断]
# words = ["apple", "banana", "cherry"]
# lengths = [len(word) for word in words]
# print(lengths)
# # 输出: [5, 6, 6]
# even_squares = [x**2 for x in range(10) if x % 2 == 0]
# print(even_squares)
# # 输出: [0, 4, 16, 36, 64]

# 元组（tuple）
# 特点：不可变的数据类型，无法修改删除增加

# t = (10, 20, 30)
# new_elem = 99
#
# # 方式 1：逗号拼接
# t = t + (new_elem,)   # 注意单元素元组必须加逗号
# print(t)              # (10, 20, 30, 99)
#
# # 方式 2：拆包语法
# t = (*t, new_elem)
# print(t)              # (10, 20, 30, 99)
#
# # 字典：一种可变的数据，可以存放任意数据类型
# 格式 dict = {'age':12 , 'sex':'male', 'height':70 , 'weight':80 }
# key唯一，是不可变数据类型。
# 新数据会覆盖旧数据
# 字典元素查找，通过key 查找value
# dict = {'key':1,}
# print(dict['key'])
# 获取字典的值
# 字典.get(key)
# 如果找不到数据，不报错
# 修改字典元素，只能修改value
# 字典[key] = 新数据
# 增加元素
# 字典[新key] = 新数据
#字典.update（可迭代对象）以键值对形式存入字典
# date = {'weight': 120 ,'height' : 男}无论一个键值对或者多个都要用{}
# 删除字典元素
# del 字典[key]
# 如果key不存在，会报错
#
# 清除字典所有元素
# 字典.clear()
# 取出字典中所有key
# 字典.keys()
# 取出字典所有键值对
# 字典.items()

# len(字典)计算键值对的数量

# 集合：一个无序不重复的元素序列
# set = {1,2,3,4,5,6,7,8,9}
# 使用{}包围数据，里面不能有键值对，数据里的元素必须是不可变数据类型，如果是可变数据，会报错
# 打印会自动去除集合中的重复元素
# 定义空集合只能用()
# 使用update进行上传元素
# 删除元素用discard   移除元素不存在也不会报错
#    每一次打印集合，元素都会打乱顺序显示
# 集合运算符&交集  |并集  -差集

# int(数据)将数据转换成整数
# 要求：整数，浮点数，被引号包围的整数

# float()将数据转换为浮点数
# 整数会在后面补一位小数点0
# 要求：整数，浮点数，被引号包围的整数和浮点数

# bool(数据)将数据转换为布尔值
# 非零非空为Ture，为零为空是False

# str(数据)将任何对象变为可打印，可拼接的字符串
# class Point:
#     def __init__(self, x, y):
#         self.x, self.y = x, y
#
#     def __str__(self):          # 给用户看
#         return f'({self.x}, {self.y})'
#
#     def __repr__(self):         # 给开发者看
#         return f'({self.x}, {self.y})'
#
# p = Point(4, 4)
# print(str(p))   # (3, 4)
# print(repr(p))  # (3, 4)


# eval()将字符串转换为可表达式
# str = "10+10"
# print(str)
# print(eval(str))

# list(可迭代对象)
# 可迭代对象：字符串，列表，字典，元组，集合。
# print(list('hello'))
# print(list({1:'a',2: 'b'}))


# tuple(可迭代对象)
# print(tuple('hello'))
# print(tuple({1:'a',2: 'b'}))

# dict(可迭代对象)元素长度必须为2 key：value
# 格式1
# print(dict([(1,'a'),(2,'b'),(3,'c'),(4,'d')]))
# print(dict({(1,'a'),(2,'b'),(3,'c'),(4,'d')}))
# print(dict(((1,'a'),(2,'b'),(3,'c'),(4,'d'))))
# {}字典
# []列表
# ()元组
# 格式2： dict(k1=v1,k2=v2)
# 注意点：key只能作为字符串，不能为其他类型
# dict(k1=1,k2=2)
# print(dict(k1=1,k2=2))

# set(可迭代对象)
# 注意点：元素必须不可变的
# print(set({1: 'a', 2: 'b'}))#只取key
# print(set(('hello')))

# zip(对象1，对象2)
# 将多个可迭代对象中的元素取出或存放到元组中，然后可以转换为对应数据类型
#如果两个可迭代对象个数不一样，按最短的来
# lst_num = [1,2,3]
# lst_s = ['a', 'b', 'c']
# lst = set(zip(lst_num,lst_s))
# print(lst)



# 不可变数据：数值型（int/float/complex/bool），字符串(str)，元组（tuple）
# 数据在修改过程，地址发生变化，修改后的数据存放在新地址
# s = 'hello'
# new_s = s.replace('hello', 'world')
# print(f'数据内容:{s}，地址{id(s)}')
# print(f'新数据:{new_s}，地址{id(new_s)}')
# 修改数据，数据所在地址一定会发生变化，定义新变量接受修改后的数据


# 可变数据：列表（list），字典（dict），集合（set）
# 在数据修改过程，地址不变
# 修改数据不会发生地址变化，打印原变量就可以输出修改后的数据

# 浅拷贝：内容是原始对象中元素的引用的拷贝，只是拷贝了引用并没有复制原对象里的数据。

# 目的：备份数据，保证数据互相独立
# 浅拷贝
# 格式：copy.copy(数据)
# new_copy = copy.copy(数据)
# 创建新对象，但新对象中的元素是对原对象中元素的引用
# 不可变元素修改后，会创建新地址。
# 使用之前要用import copy引用
# 数据备份成功，创建了新地址
# 浅拷贝后，元素地址相同，修改可变元素，备份与原始之间互相影响
# 使用场景：适用于简单对象，复制速度更快，占用空间更少


# 深拷贝：指创建一个新的对象，并且递归地复制原对象中包含的所有元素。
# 格式：copy.deepcopy(数据)
# 每个元素会有新地址
# new_deep = copy.deepcopy(数据)
# 深拷贝后，可变元素地址不同，如果修改可变元素，原始和备份数据之间互相独立


# 函数，把代码封装到函数中，后续无需代码，只用函数，即可实现调用。
# 格式：def 函数名()
#     代码
# 调用语法：函数名()
# pass占位符，如果没想好函数代码如何写，可以先用占位符。
# def login(name,password):
#     user = input('请输入用户名：')
#     pwd = input('请输入密码：')
#     if user == 'admin' and pwd == 'admin123':
#         print('登录成功')
#     else:
#         print('登录失败')
# 位置参数：调用函数传入的实际参数需要根据形式函数的位置和个数进行传参
# 如果个数不一致会报错，位置不对会内容混乱


# 关键词参数：调用函数时，不需要按照位置来传参，但个数还是要一致。
# 函数名(age = 18, name = '张三')

# 默认参数：调用函数，如果没有传参，可以使用默认值，传新参时，新会覆盖旧的
# def func(name,age=18默认值必须在后面)

# 可变参数
# 调用函数时，传入参数个数不确定
# *args/arguments/传入的数据以元组形式存放

# 可变关键字参数
# 可变参数+关键字参数-->以字典形式存储数据
# def func(**kwargs):
    # print(f'数据内容：{kwargs},类型：{type(kwargs)}')
# 将传入数据和关键字绑定在一起，数据更加清晰
# func(key=12,shfj=456)

# 函数返回值
# return 数据
# return后面的代码不会执行，如果没有return，默认返回None
# 将函数执行完成后的数据，返回给函数调用者
# 多个返回值的时候，数据以元组的形式存放
# def func():
#     num1 = int(input('数字1:'))
#     num2 = int(input('数字2:'))
#     s = num1 + num2
#     return num1, num2, s
# # a = func()
# print(a,type(a))
# x1, y1, x2= func()
# print(x1, y1, x2)

# 作用域, 变量在程序中的可访问范围
# 定义在函数内部的变量/局部变量
# 只能在函数内部使用,使用完就会销毁在外部使用会报错
# 全局变量:
# 在函数内外都可以使用


# 修改局部变量为全局变量
# global 局部变量
# 数据变量查找,就近去找

# 作用域
# 全局作用域,局部作用域,嵌套作用域

# nonlocal 将内层的变量,声明为上一次函数的变量,但无法作为全局变量
# 只能在函数内生效
# 如果上一层没有函数,是全局作用域,会报错

#
# 异常处理
# 语法1
# try:
#     可能异常的代码
# except 异常类型:
#     异常处理

# 如果记不住异常类型
# try:
#     num = int(input("Enter a number: "))
#     print("The number is:", num)
# except Exception as e: #将错误的提示信息存放到e
#     print("The error is:", e)
#     print("只能输入数字")
# else:
#     print("The number is:", num)

# try:
#     可能存在异常的代码
# except 异常类型：
#     异常处理
# else:
#     正常代码
# finally:
    # 一定会执行的代码

# 常见的异常类型
# 数据转换报错
# ValueError
# 使用的方法在数据类型中不存在
# AttributeError
# 除法中分母为0
# ZeroDivisionError
# 使用变量未定义
# NameError
# 代码不符合规范
# SyntaxError
# 字典查找的或删除key不存在
# KeyError
# 有序数据中索引值超出范围
# IndexError
# 冒号缩进不一致
# IndentationError


# 用户自定义的异常----raise
# 格式：raise Exceptions（错误提示信息）

# try:
#     user = input('请输入用户名：')
#     if len(user) >= 20:
#         raise Exception('用户名不能超过20个字符')
#     else:
#         print(f'用户名{user}注册成功')
#
# except Exception as e:
#     print(e)

# 模块
# 一种简单的代码封装和重用机制，通常保存在.py文件中，每个文件一个模块
# 内置模块
# 1）copy模块
# 作用：处理数据的深浅拷贝
# 深：copy.deepcopy()
# 浅：copy.copy()

# 2)math模块
# 作用：提供数学相关的函数
# 圆周率：math.pi
# 向上取整：math.floor()
#   下   ：math.ceil()
# 绝对值：math.fabs()
# 几次方：math.pow()
# 平方根：math.sqrt()

# 3)random模块
# 提供随机数的方法
# 获取0-1之间的随机浮点数random.random()
# 获取指定范围的随机浮点数random.uniform()
# 获取指定范围内的随机整数random.choice(序列)
# 可迭代对象中随机获取指定个数的可重复元素：random,choices(可迭代对象，k=个数)
# 不重复元素：random.sample(range(10), 10)
# 打乱有序对象中的元素位置：random.shuffle(range(10))

# 4)string模块
# 处理字符串相关的常量和工具的函数
# 所有的大小写字母：string.ascii_letters
     # 小写字母：string.ascii_lowercase
     # 大写字母：string.ascii_uppercase
# 0-9十进制数：string.digits


# 区分python文件是脚本还是
# __name__ = "__main__"
# __name__ ="模块名"

# 模块导入方式
# import 模块名
# 导入模块中的全部数据，使用模块中的数据时，数据前面要带上模块
# 模块 import 函数/类名/常量
# random import randint
# 导入模块中的指定数据，数据前面不带模块名，带上会报错
# 只能使用导入的数据
# from random import randint
# print(randint(1, 100))
# print(random.randint(1, 100))不能带模块名


# 3)导入模块后起别名    一般不用
# import random as rand


# 包：组织模块的一种方式，包含多个模块和子包的目录
# 存放位置：lib/site_packages
# 创建包：
    # 自动创建__init__.py文件作用是将目录识别成包(文件为空，但必须存在)
    # 删除这个文件，包会变成文件夹

# 导入包：使用包中的数据
# import 包名.模块名
# from 包名 import 模块名
# from 包名.模块名 import 函数名/变量

# 代码实现类和对象
# 定义类
# class 类名:
#     pass
#
# 创建对象
# 对象名 = 类名()

# 类名也属于标识符一种,遵循标识符的规定,且采取大驼峰命名法,类名后面加()
# class People:
#     pass
# ren = People()
# print(ren)获得对象所在地址

# 类属性：定义在类中，不在方法中的变量
# 特点：类中所有对象都能使用的属性，但只能被类名修改
# class Person:
#     name = '123'#定义在类中的变量：类属性
#
#     def func(self):
#         print('类中的方法')
# # 创建对象
# ren = Person()
# # 调用类中的类属性
# print(ren.name)
# # 修改类属性
# Person.name = '321'
# # 输出修改后的类属性
# print(ren.name)
# 类属性
# 定义：在类中，但不作为方法的参数
# 修改：只能通过类名修改，对象修改类属性，修改的只是对象自身的数据
# 类外调用：可以通过对象调用，也可以通过类名
# 类中调用：对象在类中名称是self

# 实例属性--构造方法
# 在创建对象时，自动调用该方法，用于初始化对象的属性
# 目的：给对象传值
# 名字是固定的：__init__(self)
# def __init__(self,sex,age,height,weight):
#     self.sex = sex
#     self.age = age
#     self.height = height
#     self.weight = weight

# 实例属性
# 定义：定义在构造方法中的参数
# 修改：只能通过对象修改
# 类外调用：只能通过对象调用
# 类中：只能通过self

# 构造方法：
# 定义：在类中的函数，方法名固定__init__(self)
# 第一个参数固定是self，表示对象的引用
# 目的：在创建对象时自动执行，为对象绑定属性
# 查看对象的实例属性：__dict__,将对象的实例属性以字典形式返回

# 魔术方法
# __str__()
# 创建一个类，打印对象时，得到一个对用户友好的字符串
# 返回数据必须是字符串形式，否则会报错
# class Student(object):
#     pass
#     def __str__(self):
#         return '魔术方法'
#
# s = Student()
# print(s)打印出来魔术方法

# __del__()
# 析构函数，用于显式删除对象，对象在调用完之后，会被python的垃圾回收机制，自动销毁。


# 面向对象三大特征：封装、继承、多态
# 封装
# 隐藏对象的内部细节，仅暴露必要的接口，以提高代码安全性
# 命名规则
# 什么也不加。普通属性和方法，外部可直接访问。
# _，以单英文下划线开头，表示“不建议外部直接访问”，但不会阻止
# __双英文下划线开头，表示不能在外部直接访问
#
# class BankAccount:
#     """一个极简封装示例：银行帐户"""
#
#     def __init__(self, owner: str, initial: float = 0.0):
#         # 伪私有属性：外部无法直接访问
#         self.__balance = float(initial)
#         self.owner = owner
#
#     # 存钱接口
#     def deposit(self, amount: float) -> None:
#         if amount <= 0:
#             raise ValueError("存入金额必须 > 0")
#         self.__balance += amount
#         print(f"[存入] {amount:.2f} 元成功")
#
#     # 取钱接口
#     def withdraw(self, amount: float) -> None:
#         if amount <= 0:
#             raise ValueError("取出金额必须 > 0")
#         if amount > self.__balance:
#             raise ValueError("余额不足")
#         self.__balance -= amount
#         print(f"[取出] {amount:.2f} 元成功")
#
#     # 查余额接口（只读）
#     def get_balance(self) -> float:
#         return self.__balance
#
#     # 优雅打印
#     def __str__(self) -> str:
#         return f"{self.owner} 的帐户余额：{self.get_balance():.2f} 元"
#
#
# # ------------------- 演示 -------------------
# if __name__ == "__main__":
#     ac = BankAccount("Kimi", 100)
#     print(ac)                       # 查看余额
#
#     ac.deposit(15.5)
#     print(ac)
#
#     ac.withdraw(30)
#     print(ac)

# 继承：子类可以复制父类的代码，减少代码的冗余
# 子类中没有的属性和方法，通过继承，可以使用父类的属性和方法，私有数据没法继承

# 方法重写
# 子类在父类的基础上进行修改
# 参数格式，位置要一致，会覆盖父类的方法
# 1，同名方法覆盖
# 2.suoer().方法名
# 3.父类名.方法名(self)

# 多继承，子类有多个父类
# class zi(fu1,fu2):
    # pass
# 查看继承顺序__mro__从fu1,fu2从左到右开始查找
# print(zi.__mro__)
# 如果找到了，直接执行，不再搜索
# 如果找到最后一个object还未找到，报错

# 多态
# 允许不同类的对象使用相同的方法得到不同的响应
# 一个接口多种实现:方法的重写

# 属性:实例属性,类属性
# 方法:实例方法,构造方法,类方法,静态方法
# 实例方法：绑定给对象的方法，第一个参数必须是self，表示对象的引用
# 构造方法：在创建对象时自动执行的方法
# 类方法：绑定给类的方法，第一个参数必须是cls，表示类的引用
# 静态方法：谁也没有绑定，独立的功能函数

# 类方法：在方法定义的上方，使用@classmethod装饰方法
# class Company:
#     name = 'pyhton'
#     @classmethod
#     def f(cls):
#         pass
# 使用场景：希望在类中修改类属性，希望在创建对象之前，验证对象是否满足要求

# 静态方法：功能独立的函数
# 使用场景：相应功能与类和实例都无关，本质就是一个定义在类中的普通函数
# @staticmethod

# 打开文件open(文件路径)
# 读写read ， write
# 关闭文件close
# 文件 = open("文件所在位置")
# 文件.read()读取所有内容
#      .readline读取一行内容
#        .readlines读取所有行,返回数据类型是字符串列表
# 文件指针,第一次读取在文件的开头,第二次及以后指针在结尾
# 重置文件指针
# 文件.seek(0)

写入数据
# 文件.write(str)一次写入一个字符串的数据
# 文件.writelines()一次写入多个数据

# r只读模式,不能修改文件内容
# w只写模式,不能读取文件内容
# a追加模式,往文件末尾追加新的数据

# with上下文管理器
# 打开文件后,对文件操作完,自动关闭文件的操作
# with open("文件位置","模式",encoding="utf-8") as f:
#     pass
# print(f.closed)True=文件关闭,False = 文件打开

# os模块：复杂程序与操作系统的交互
# import os
# print(os.listdir())获取当前目录下所有的文件
# print(os.getcwd())获取当前目录的绝对路径
# print(os.mkdir("文件名字"))在当前目录下创建子目录，如果目录存在会报错
# print(os.rmdir("文件名字"))只能删除空目录，非空会报错
# (os,remove("文件路径"))删除文件
# (os.path.isfile("路径"))判断数据是否为文件，是为True，不是为False
# (os.path.exists("路径"))检查路径是否存在，存在为True，不存在为False

# 正则表达式为re模块
# pattern是正则表达式
# string是匹配的字符串
# re.findall(pattern =,string =)

# .匹配除换行符以外的任意字符
# \w匹配字母或数字或下划线
# \s匹配任意空白符
# \d 匹配任意数字
# \n 匹配一个换行符
# \t 匹配一个制表符
# ^ 匹配字符串开始
# $ 匹配一个制表符

# \W匹配非字母或数字下划线
# \D 匹配非数字
# \S匹配非空白字符
# a|b 匹配字符a或b
# () 匹配括号内的表达式，也表示一个组
# [...] 匹配字符组中的字符
# [^...]匹配除了字符组中的字符其他字符

#      量词
# * 重复0次或更多次
# + 重复1次或更多次
# ? 重复0或1次
# {n}匹配n次
# {n,}匹配n次或更多
# {n,m}重复匹配n到m次

# 匿名方法只能实现简单的逻辑，如果函数只有一个返回值并且只有一句代码，可以使用lambda
# 使用很少基本调用一次# 数字列表自定义排序
# numbers = [3, -1, 4, -2, 5, -3]
# numbers.sort(key=lambda x: abs(x))  # 按绝对值排序
# print(numbers)  # [-1, -2, -3, 3, 4, 5]

# # 字符串列表自定义排序
# words = ['apple', 'Banana', 'cherry', 'Date']
# words.sort(key=lambda x: x.lower())  # 忽略大小写排序
# print(words)  # ['apple', 'Banana', 'cherry', 'Date']

# bin()二进制转换
# oct()八进制转换
# hex()十六进制转换

# 保留小数用round(浮点数,几位小数)